/**
 * @file jump32.c
 *
 * @brief jump function for tinymt32
 *
 * Jump function changes the internal state vector of tinymt32
 * pseudorandom number generator to the state which is N step after
 * current state, as if it generated N random numbers.  The jump is
 * much faster than generating N numbers, when N is large.
 *
 * @author Mutsuo Saito (Hiroshima University)
 * @author Makoto Matsumoto (The University of Tokyo)
 *
 * Copyright (C) 2011, 2012 Mutsuo Saito, Makoto Matsumoto,
 * Hiroshima University and University of Tokyo.
 * All rights reserved.
 *
 * The 3-clause BSD License is applied to this software, see
 * LICENSE.txt
 */
#include "jump32.h"
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>

static void tinymt32_add(tinymt32_t *dest, const tinymt32_t *src);

/**
 * Addition of internal state as F<sub>2</sub> vector.
 * @param dest destination
 * @param src source
 */
static void tinymt32_add(tinymt32_t *dest, const tinymt32_t *src)
{
    dest->status[0] ^= src->status[0];
    dest->status[1] ^= src->status[1];
    dest->status[2] ^= src->status[2];
    dest->status[3] ^= src->status[3];
}

/**
 * jump function
 * @param tiny tinymt32 structure, overwritten by new state after calling
 * this function.
 * @param lower_step lower bit of 128-bit integer
 * @param upper_step upper bit of 128-bit integer
 * @param poly_str string of the characteristic polynomial generated by
 * tinymt32dc
 */
void tinymt32_jump(tinymt32_t *tiny,
		   uint64_t lower_step,
		   uint64_t upper_step,
		   const char * poly_str)
{
    f2_polynomial jump_poly;

    calculate_jump_polynomial(
	&jump_poly, lower_step, upper_step, poly_str);
    tinymt32_jump_by_polynomial(tiny, &jump_poly);
}


/**
 * jump using the jump polynomial.
 * This function is not as time consuming as calculating jump polynomial.
 * This function can use multiple time for the tinymt32 structure.
 * @param tiny tinymt32 structure, overwritten by new state after calling
 * this function.
 * @param jump_poly the jump polynomial calculated by
 * tinymt32_calculate_jump_polynomial.
 */
void tinymt32_jump_by_polynomial(tinymt32_t *tiny,
		   f2_polynomial * jump_poly)
{
    tinymt32_t work_z;
    tinymt32_t * work = &work_z;
    *work = *tiny;
    for (int i = 0; i < 4; i++) {
	work->status[i] = 0;
    }

    uint64_t x64 = jump_poly->ar[0];
    for (int i = 0; i < 64; i++) {
	if ((x64 & 1) != 0) {
	    tinymt32_add(work, tiny);
	}
	tinymt32_next_state(tiny);
	x64 = x64 >> 1;
    }
    x64 = jump_poly->ar[1];
    while (x64 != 0) {
	if ((x64 & 1) != 0) {
	    tinymt32_add(work, tiny);
	}
	tinymt32_next_state(tiny);
	x64 = x64 >> 1;
    }
    *tiny = *work;
}
